In the previous Marching Cubes tutorial a basic Marching Cubes algorithm was shown and a sample C++ source code was provided. Here a couple of changes are made to the original code which improve the way the light is computed and increase its speed.
A screen shot of Marching Cubes in action in the previous tutorial shows the blocky appearance of the rendered object. This is due to the fact that the normals were computed as a cross product of two sides of a triangle. This gives a vector normal to each triangular face, but not a normal to the original surface, which is what is needed. To compute the normal to the surface rendered, gradient vectors are used. A gradient is computed at each vertex and then is linearly interpolated to the vertices on the edges, just like the vertices. This will give a normal vector orthogonal to the surface instead of a triangle, which is used to approximate the surface. Gradient vector at vertex (i, j, k) of a surface defined by function f is computed according to the formula:
Another aspect on which the original Marching Cubes algorithm can be improved is its speed. So far all the cubes in the defined space were "marched" through, with those being intersected by the surface subdivided into triangles. However, unless the surface is really complex or there are few subdivisions, most of the cubes are not intersected by the surface. To limit the amount of "empty" cubes that are looked at, a recursive Marching Cubes Algorithm is considered. Starting from a cube which is known to be intersected the algorithm tests cubes adjacent to its 6 surfaces. The cube is not considered by the algorithm if its on the bounday or has already been looked at. If the cube is not intersected then it returns to the previous one and does not launch recursive calls on its own 5 remaining faces. The algorithm stops when the cubes on all of its 6 faces have either been already "marched" through or are not considered. This way only the cubes on and adjacent to the surface are tested. The drawback of this algorithm comes up when multiple objects are drawn. Unless we know the intersecting cubes for each object, only one object can be drawn (objects are considered separate when they don't share an intersecting cube).
In my source code I pass other information from cube to cube, such as the vertices and gradient vectors at each vertex, interpolated vertices and gradient vectors on the adjacent edges. These values are passed as arrays of length four, since there are only four edges/vertices that two adjacent cubes can have. The face numbers and the way the indices of the passed values correspond to those of the current cube are shown in the figure below:
Please email me with suggestions and/or bugs at:
myp@andrew.cmu.edu or mikepolyakov@hotmail.com